import java.io.*;
import java.sql.*;
import java.util.*;

public class Main {
	private static int ITERATIONS = 5;
	
	private static void InitDB() {
		Connection connection = DB.Connect();
		try {
			Statement stmt = connection.createStatement();
			stmt.execute(QueryGenerator.preAllQ());
		} catch (SQLException e1) {
			System.out.println("Query initialization failed");
			e1.printStackTrace();
			return;
		}
		DB.Disconnect(connection);
	}
	
	private static void DeInitDB() {
		Connection connection = DB.Connect();
		try {
			Statement stmt = connection.createStatement();
			stmt.execute(QueryGenerator.postAllQ());
		} catch (SQLException e1) {
			System.out.println("Query initialization failed");
			e1.printStackTrace();
			return;
		}
		DB.Disconnect(connection);
	}
	
	private static double ComputeMedianResponseTime(ArrayList<Result> results) {
		long []ms = new long[results.size()];
		int i = 0;
		for(Result r : results) {
			ms[i++] = r.getMilliseconds();
		}
		Arrays.sort(ms);
		if(ms.length % 2 == 1) {
			// Odd number
			int middle = (ms.length - 1) / 2;
			return ms[middle];
		} else {
			// Even number
			int middle1 = ms.length / 2 - 1;
			int middle2 = ms.length / 2 ;
			return ((double)(ms[middle1] + ms[middle2])) / 2;
		}
	}
	
	private static double ComputeAverageResponseTime(ArrayList<Result> results) {
		long sum = 0;
		for(Result r : results) {
			sum += r.getMilliseconds();
		}
		return ((double)sum) / results.size();
	}
	
	public static void main(String[] args) {
		
		if(args.length < 3) {
			System.out.println("Please use arguments:");
			System.out.println("<arg1>: workload1 | workload2 | workload3");
			System.out.println("<arg2>: # of clients threads");
			System.out.println("<arg3>: experiment ID");
			return ;
		}
		
		int clientsCount = Integer.parseInt(args[1]);
		String workload = args[0].toLowerCase();
		String experimentID = args[2].toLowerCase();
		
		Client []clients = new Client[clientsCount];
				
		InitDB();
		
		if(workload.equals("workload1") || workload.equals("workload2")) {
			for(int i=0; i<clients.length; i++) {
				if(workload.equals("workload1")) {
					clients[i] = new ClientWorkLoad1(ITERATIONS, i);
				} else if(workload.equals("workload2")) {
					clients[i] = new ClientWorkLoad2(ITERATIONS, i);
				}
			}
			
			for(int i=0; i<clients.length; i++) {
				clients[i].start();
			}
			
			for(int i=0; i<clients.length; i++) {
				try {
					clients[i].join();
				} catch (InterruptedException e) {
					System.out.println("Client "+i+" was interrupted:");
					e.printStackTrace();
				}
			}
		} else if(workload.equals("workload3")) {
			int updateToUse = 1;
			QueryGenerator.GenerateUpdates(ITERATIONS * clientsCount);
			for(int iteration=0; iteration<ITERATIONS; iteration++) {
				
				for(int i=0; i<clients.length; i++) {
					ArrayList<Result> prevResult = new ArrayList<Result>();
					if(clients[i] != null) {
						prevResult = clients[i].GetResult();
					}
					clients[i] = new ClientWorkLoad3(ITERATIONS, iteration, i, prevResult);
				}
				
				for(int i=0; i<clients.length; i++) {
					clients[i].start();
				}
				
				for(int i=0; i<clients.length; i++) {
					try {
						clients[i].join();
					} catch (InterruptedException e) {
						System.out.println("Client "+i+" was interrupted:");
						e.printStackTrace();
					}
				}
				
				for(int i=0; i<clients.length; i++) {
					((ClientWorkLoad3)clients[i]).runUpdates(updateToUse++);
				}
			}
		} else {
			System.out.println("Unknown workload argument: Usage: ");
			System.out.println("Please use arguments:");
			System.out.println("<arg1>: workload1 | workload2 | workload3");
			System.out.println("<arg2>: # of clients threads");
			return ;
		}
		
		DeInitDB();

		ArrayList<Result> results = new ArrayList<Result>();
		for(int i=0; i<clients.length; i++) {
			results.addAll(clients[i].GetResult());
		}
		
		try {
			FileWriter fstream = new FileWriter("experiment"+experimentID+".txt",false);
			BufferedWriter outFile = new BufferedWriter(fstream);
			for(Result r : results) {
				outFile.write(r + "\n");
			}
			outFile.close();
		} catch (Exception e) {
			System.out.println("Can't write to experiment file!!!");
			e.printStackTrace();
		}
		

		double throughput = 0;
		for(Client c : clients) {
			throughput += c.GetThroughput();
		}
		double medianResponseTime = ComputeMedianResponseTime(results);
		double averageResponseTime = ComputeAverageResponseTime(results);
		
		try {
			FileWriter fstream = new FileWriter("experiments.txt",true);
			BufferedWriter outFile = new BufferedWriter(fstream);
			outFile.write(experimentID + "," + workload + "," + clientsCount + "," + throughput + "," + medianResponseTime + "," + averageResponseTime + "\n");
			outFile.close();
		} catch (Exception e) {
			System.out.println("Can't write to experiments file!!!");
			e.printStackTrace();
		}
	}
}
